#include "checkers/FakeCachelineShare.h"


extern ReportSpace::Report external_bug_report;


void FakeCachelineShare::check() {
  // basic algorithm: 1. retrieve all read/write info of structures.
  // 2. iterating through each structure and report if | avg_read - avg_write| < threshold
  readConfig();
  analyzeStruct();

}

void FakeCachelineShare::readConfig() {
  std::unordered_map<std::string, std::string> ptrConfig =
      configure->getOptionBlock("FakeCachelineShareChecker");
  // The higher it is, the more bugs it will report
  Threshold = stof(ptrConfig.find("min_gap")->second);
  Min_Access = stoi(ptrConfig.find("min_access")->second);
  const std::string &t = ptrConfig.find("DefaultLoopCount")->second;
  DefaultLoopCount = atoi(t.c_str());
}

int FakeCachelineShare::computeAccessFreq(const std::vector<int>& readArray, const std::vector<int>& writeArray) {
    for (int i = 0; i <readArray.size(); i++) {
        float val = readArray[i];
        if (val < Min_Access)
            continue;
        for (int j = 0; j < writeArray.size(); j++) {
            float wv = writeArray[j];
            if ( i == j || wv < Min_Access)
                continue;
            if (min(val,wv)/max(val, wv) > Threshold) {
                return i;
            }
        }
    }
    return -1;
}

void FakeCachelineShare::analyzeRes(std::vector<std::pair<const RWAccess*, const VarDecl*>> &pairs){
    for (auto pair : pairs) {
        const RWAccess *ptr = pair.first;
        int res = computeAccessFreq(ptr->readArray, ptr->writeArray);
        if (res != -1) {
            const VarDecl *D = pair.second;
            const clang::SourceManager &SM = D->getASTContext().getSourceManager();
            SourceLocation SL = D->getBeginLoc();
            unsigned int lineNum = SM.getExpansionLineNumber(SL);
            unsigned int columnNum = SM.getExpansionColumnNumber(SL);
            string filename = SL.printToString(SM);
            ReportSpace::Defect *def = new ReportSpace::Defect(ReportSpace::DefectName::FakeCachelineShare,
                                                               ReportSpace::DefectType::Error,
                                                               filename, lineNum, columnNum,
                                                               D->getQualifiedNameAsString());
            def->addDesc("Fake Cacheline Sharing in variable " + D->getQualifiedNameAsString());
            external_bug_report.addToReport(def);
        }
    }
}

void FakeCachelineShare::analyzeStruct() {
    std::vector<ASTFile *> astFiles = resource->getASTFiles();
    int count = 1;
    for(ASTFile * astFile : astFiles)
    {
        process_bar(float(count++) / astFiles.size());
        ASTUnit * astUnit = manager->getASTUnit(astFile);
        ASTContext& AST = astUnit->getASTContext();
        StructureAnalyzer analyzer(&AST, configure);
        analyzer.setDefaultLoopCount(DefaultLoopCount);
        analyzer.analyze();
        auto &frequentVar = analyzer.retFrequentVar();
        analyzeRes(frequentVar);
    }
}
